#include "jdebug.h"
#include <atomic>

namespace JXQ
{

class J_DECL_HIDDEN JDebugPrivate
{
public:
	inline JDebugPrivate(JDebug::Channel c) : c(c) {}
	JDebug::Channel c;
};

/*-------------------------------------------------------------------------*/

JDebug::JDebug(Channel c) :
	d_ptr(new JDebugPrivate(c))
{

}

static std::atomic_bool gWordWrap(true);

JDebug::~JDebug()
{
	if( gWordWrap )
	{
		if( d_ptr->c == StdOut )
			std::cout << std::endl;
		else if( d_ptr->c == StdErr )
			std::cerr << std::endl;
		else if( d_ptr->c == Log )
			std::clog << std::endl;
	}

	delete d_ptr;
}

JDebug &JDebug::setChannel(Channel c)
{
	d_ptr->c = c;
	return *this;
}

JDebug::Channel JDebug::channel() const
{
	return d_ptr->c;
}

void JDebug::setWordWrap(bool enable)
{
	gWordWrap = true;
}

bool JDebug::wordWrap()
{
	return gWordWrap;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const char _c)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << "'" << _c << "' ";
	else if( c == d.StdErr )
		std::cerr << "'" << _c << "' ";
	else if( c == d.Log )
		std::clog << "'" << _c << "' ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const char _c)
{
	return d << _c;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const wchar_t wc)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::wcout << L"'" << wc << L"' ";
	else if( c == d.StdErr )
		std::wcerr << L"'" << wc << L"' ";
	else if( c == d.Log )
		std::wclog << L"'" << wc << L"' ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const wchar_t wc)
{
	return d << wc;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const bool &b)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << (b? "true" : "false") << " ";
	else if( c == d.StdErr )
		std::cerr << (b? "true" : "false") << " ";
	else if( c == d.Log )
		std::clog << (b? "true" : "false") << " ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const bool &b)
{
	return d << b;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const char *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << str << " ";
	else if( c == d.StdErr )
		std::cerr << str << " ";
	else if( c == d.Log )
		std::clog << str << " ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const char *str)
{
	return d << str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, char *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << str << " ";
	else if( c == d.StdErr )
		std::cerr << str << " ";
	else if( c == d.Log )
		std::clog << str << " ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, char *str)
{
	return d << str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const wchar_t *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::wcout << str << L" ";
	else if( c == d.StdErr )
		std::wcerr << str << L" ";
	else if( c == d.Log )
		std::wclog << str << L" ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const wchar_t *str)
{
	return d << str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, wchar_t *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::wcout << str << L" ";
	else if( c == d.StdErr )
		std::wcerr << str << L" ";
	else if( c == d.Log )
		std::wclog << str << L" ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, wchar_t *str)
{
	return d << str;
}

#ifndef _MSC_VER

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const std::basic_string<char> *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << '"' << str->c_str() << "\" ";
	else if( c == d.StdErr )
		std::cerr << '"' << str->c_str() << "\" ";
	else if( c == d.Log )
		std::clog << '"' << str->c_str() << "\" ";
	return d;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const std::basic_string<wchar_t> *str)
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::wcout << L"\"" << str->c_str() << L"\" ";
	else if( c == d.StdErr )
		std::wcerr << L"\"" << str->c_str() << L"\" ";
	else if( c == d.Log )
		std::wclog << L"\"" << str->c_str() << L"\" ";
	return d;
}

#endif //no MSVC

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const std::basic_string<char> *str)
{
	return d << str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const std::basic_string<wchar_t> *str)
{
	return d << str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const std::basic_string<char> &str)
{
	return d << &str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const std::basic_string<char> &str)
{
	return d << &str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const std::basic_string<wchar_t> &str)
{
	return d << &str;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const std::basic_string<wchar_t> &str)
{
	return d << &str;
}

JXQLIB_EXPORT JDebug &operator<<
(JDebug &d, std::basic_ostream<char>&(*ctrl)(std::basic_ostream<char>&))
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::cout << ctrl;
	else if( c == d.StdErr )
		std::cerr << ctrl;
	else if( c == d.Log )
		std::clog << ctrl;
	return d;
}

JXQLIB_EXPORT JDebug &operator<<
(JDebug &&d, std::basic_ostream<char>&(*ctrl)(std::basic_ostream<char>&))
{
	return d << ctrl;
}

JXQLIB_EXPORT JDebug &operator<<
(JDebug &d, std::basic_ostream<wchar_t>&(*ctrl)(std::basic_ostream<wchar_t>&))
{
	auto c = d.channel();
	if( c == d.StdOut )
		std::wcout << ctrl;
	else if( c == d.StdErr )
		std::wcerr << ctrl;
	else if( c == d.Log )
		std::wclog << ctrl;
	return d;
}

JXQLIB_EXPORT JDebug &operator<<
(JDebug &&d, std::basic_ostream<wchar_t>&(*ctrl)(std::basic_ostream<wchar_t>&))
{
	return d << ctrl;
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &d, const unsigned char num)
{
	return d << J_SCT(int, num);
}

JXQLIB_EXPORT JDebug &operator<<(JDebug &&d, const unsigned char num)
{
	return d << J_SCT(int, num);
}

} //namespace JXQ

/*-----------------------------------------------------------------------------------------------------*/

#if defined(J_OS_LINUX) ||defined(J_OS_LINUX)
#include <signal.h>
#include <unistd.h>
#include <execinfo.h>

#define BACKTRACE_SIZE 16

static __sighandler_t gPreHandler = nullptr;

static void signalHandler(int signo)
{
	if( signo == SIGSEGV or signo == SIGABRT )
	{
		void *buffer[BACKTRACE_SIZE];
		int n = backtrace(buffer, BACKTRACE_SIZE);

		printf("[%s]:[%d] n = %d\n", __func__, __LINE__, n);

		char **symbols = backtrace_symbols(buffer, n);
		if( symbols == nullptr )
		{
			perror("backtrace symbols");
			exit(EXIT_FAILURE);
		}
		printf("[%s]:[%d]\n", __func__, __LINE__);

		for(int i=0; i<n; i++)
			printf("%d: %s\n", i, symbols[i]);

		free(symbols);
		exit(-1);
	}

	if( gPreHandler != nullptr )
		gPreHandler(signo);
}

static int filter_SIGSEGV()
{
	auto pre = ::signal(SIGPIPE, signalHandler);
	if( pre == SIG_ERR )
		jError() << "ERROR(from JXQLIB): The process could not register signal 'SIGPIPE(13)'";
	else
		gPreHandler = pre;
	return 0;
}
volatile int __filter_SIGSEGV__ J_DECL_UNUSED = filter_SIGSEGV();

#endif //linux/unix
